home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Newswatcher 2.0b22 / NW Source / Source / newswatcher.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-04  |  32.1 KB  |  1,449 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     newswatcher.c
  4.  
  5.     This module contains the main entry point of the program and the main 
  6.     event loop.
  7.     
  8.     Copyright © 1994, Northwestern University.
  9.     
  10. ----------------------------------------------------------------------------*/
  11.  
  12.     
  13.     
  14. #include <string.h>
  15. #include <appleevents.h>
  16. #include <stdio.h>
  17. #include <traps.h>
  18. #include <GestaltEqu.h>
  19. #include <Drag.h>
  20. #include <FragLoad.h>
  21.  
  22. #include "glob.h"
  23. #include "newswatcher.h"
  24. #include "about.h"
  25. #include "newsrc.h"
  26. #include "full.h"
  27. #include "prefsdlog.h"
  28. #include "menus.h"
  29. #include "news.h"
  30. #include "prefs.h"
  31. #include "print.h"
  32. #include "log.h"
  33. #include "net.h"
  34. #include "nntp.h"
  35. #include "message.h"
  36. #include "wind.h"
  37. #include "dialog.h"
  38. #include "aevt.h"
  39. #include "group.h"
  40. #include "subject.h"
  41. #include "article.h"
  42. #include "status.h"
  43. #include "text.h"
  44. #include "memutil.h"
  45. #include "spin.h"
  46. #include "windutil.h"
  47. #include "fileutil.h"
  48. #include "cache.h"
  49. #include "strutil.h"
  50. #include "resutil.h"
  51. #include "open.h"
  52. #include "teutil.h"
  53. #include "dummy.h"
  54. #include "sfutil.h"
  55. #include "drawutil.h"
  56. #include "ldef.h"
  57. #include "url.h"
  58. #include "subscribe.h"
  59. #include "ic.h"
  60.  
  61.  
  62.  
  63. #define kNeedSystem7AlertID        500            /* need System 7.0 or later alert id */
  64. #define kTPrefRecSize            7960        /* preferences record size */
  65.  
  66. #define kErrorDuringQuitDlg                155
  67. #define kErrorDuringQuitDlgQuitItem        1
  68.  
  69. #define kWaitForDNRDlg        154                /* wait for DNR dialog */
  70.  
  71. static UniversalProcPtr gOldExitToShellUPP;
  72. static UniversalProcPtr gMyExitToShellUPP;
  73.  
  74. static WindowPtr gMouseDownWindow = nil;    /* pointer to window in which mouse
  75.                                                down happened */
  76.  
  77.  
  78.  
  79. /*----------------------------------------------------------------------------
  80.     DoClose
  81.     
  82.     Close a window.
  83.     
  84.     Entry:    wind = pointer to window.
  85.     
  86.     Exit:    function result = error code.
  87. ----------------------------------------------------------------------------*/
  88.  
  89. OSErr DoClose (WindowPtr wind)
  90. {
  91.     TWindowKind kind;
  92.     GrafPtr port;
  93.     OSErr err = noErr;
  94.  
  95.     kind = GetWindowKind(wind);
  96.     if (kind != kNotOurWind) {
  97.         GetPort(&port);
  98.         SetPort(wind);
  99.         err = (*gDispatch[kind].close)(wind);
  100.         SetPort(port);
  101.     }
  102.     return err;
  103. }
  104.  
  105.  
  106.  
  107. /*----------------------------------------------------------------------------
  108.     DoZoom
  109.     
  110.     Zoom a window.
  111.     
  112.     Entry:    wind = pointer to window.
  113.             zoomDir = zoom direction = inZoomIn or inZoomOut.
  114.             
  115.     Exit:    function result = error code.
  116. ----------------------------------------------------------------------------*/
  117.  
  118.  
  119. OSErr DoZoom (WindowPtr wind, short zoomDir)
  120. {
  121.     TWindowKind kind;
  122.     GrafPtr port;
  123.     OSErr err = noErr;
  124.  
  125.     kind = GetWindowKind(wind);
  126.     if (kind != kNotOurWind) {
  127.         GetPort(&port);
  128.         SetPort(wind);
  129.         err = (*gDispatch[kind].zoom)(wind, zoomDir);
  130.         SetPort(port);
  131.     }
  132.     
  133.     return err;
  134. }
  135.  
  136.  
  137.  
  138. /*----------------------------------------------------------------------------
  139.     DoCommand
  140.     
  141.     Do a command.
  142.     
  143.     Entry:    mResult = 16/menu, 16/item.
  144.             modifiers = modifiers field from event record.
  145.             
  146.     Exit:    function result = error code.
  147.     
  148.     Commands which act on the frontmost window are dispatched to the
  149.     proper window handling module. Other commands are handled directly,
  150.     without invoking a window handling module.
  151. ----------------------------------------------------------------------------*/
  152.  
  153. static OSErr DoCommand (long mResult, short modifiers)
  154. {
  155.     short menu, item;
  156.     WindowPtr wind;
  157.     TWindowKind kind;
  158.     GrafPtr port;
  159.     Boolean commandHandled;
  160.     Str255 daName;
  161.     OSErr err = noErr;
  162.     
  163.     menu = HiWord(mResult);
  164.     item = LoWord(mResult);
  165.     
  166.     wind = FrontWindow();
  167.     kind = GetWindowKind(wind);
  168.     
  169.     commandHandled = true;
  170.     
  171.     switch (menu) {
  172.     
  173.         case kAppleMenu:
  174.         
  175.             switch (item) {
  176.                 case kAboutNewsWatcherItem:
  177.                     err = DoAboutNewsWatcher();
  178.                     break;
  179.                 default:
  180.                     GetPort(&port);
  181.                     SetCursor(&qd.arrow);
  182.                     GetMenuItemText(GetMenuHandle(kAppleMenu), item, daName);
  183.                     OpenDeskAcc(daName);
  184.                     SetPort(port);
  185.                     break;
  186.             }
  187.             break;
  188.             
  189.         case kFileMenu:
  190.         
  191.             switch (item) {
  192.                 case kNewGroupWindowItem:
  193.                     err = DoNewGroupWindow();
  194.                     break;
  195.                 case kOpenItem:
  196.                     err = DoOpen();
  197.                     break;
  198.                 case kCloseItem:
  199.                     err = DoClose(wind);
  200.                     break;
  201.                 case kPageSetupItem:
  202.                     err = DoPageSetup();
  203.                     break;
  204.                 case kPreferencesItem:
  205.                     err = DoPreferences();
  206.                     break;
  207.                 case kQuitItem:
  208.                     gDone = true;
  209.                     break;
  210.                 default:
  211.                     commandHandled = false;
  212.                     break;
  213.             }
  214.             break;
  215.             
  216.         case kEditMenu:
  217.         
  218.             commandHandled = false;
  219.             break;
  220.  
  221.         case kNewsMenu:
  222.         
  223.             switch (item) {
  224.                 case kNewMessageItem:
  225.                     err = DoNewMessage();
  226.                     break;
  227.                 default:
  228.                     commandHandled = false;
  229.                     break;
  230.             }
  231.             break;
  232.  
  233.         case kSpecialMenu:
  234.         
  235.             switch (item) {
  236.                 case kGetGroupListFromHostItem:
  237.                     err = DoGetGroupListFromHost();
  238.                     break;
  239.                 case kGetServerInfoItem:
  240.                     err = DoGetServerInfo();
  241.                     break;
  242.                 case kOpenURLItem:
  243.                     err = DoOpenURL(wind);
  244.                     break;
  245.                 case kCheckForNewGroupsItem:
  246.                     err = DoCheckForNewGroups();
  247.                     break;
  248.                 case kCheckForDeletedGroupsItem:
  249.                     err = DoCheckForDeletedGroups();
  250.                     break;
  251.                 case kRebuildFullGroupListItem:
  252.                     err = DoRebuildFullGroupList();
  253.                     break;
  254.                 default:
  255.                     commandHandled = false;
  256.                     break;
  257.             }
  258.             break;
  259.  
  260.         case kWindMenu:
  261.         
  262.             switch (item) {
  263.                 case kCycleWindowsItem:
  264.                     DoCycleWindows(wind);
  265.                     break;
  266.                 case kZoomWindowItem:
  267.                     err = DoZoomWindow(wind);
  268.                     break;
  269.                 case kShowHideFullGroupListItem:
  270.                     err = DoShowHideFullGroupList();
  271.                     break;
  272.                 default:
  273.                     SelectWindMenu(item);
  274.                     break;
  275.             }
  276.             break;
  277.             
  278.      }
  279.     
  280.     if (!commandHandled && kind != kNotOurWind) {
  281.         GetPort(&port);
  282.         SetPort(wind);
  283.         err = (*gDispatch[kind].command)(wind, menu, item, modifiers);
  284.         SetPort(port);
  285.     }
  286.      
  287.     HiliteMenu(0);
  288.     
  289.     return err;
  290. }
  291.  
  292.  
  293.  
  294. /*----------------------------------------------------------------------------
  295.     Draggable
  296.     
  297.     Determine whether a mouse down event is in a draggable object in a 
  298.     window.
  299.     
  300.     Entry:    wind = pointer to window.
  301.             where = location of mouse down event, in local coordinates.
  302.             modifiers = modifiers field from event record.
  303.             
  304.     Exit:    function result = true if object is draggable.
  305. ----------------------------------------------------------------------------*/
  306.  
  307. static Boolean Draggable (WindowPtr wind, Point where, short modifiers)
  308. {
  309.     GrafPtr port;
  310.     TWindowKind kind;
  311.     Boolean result = false;
  312.  
  313.     if (!gHaveDragMgr) return false;
  314.     kind = GetWindowKind(wind);
  315.     if (kind != kNotOurWind) {
  316.         GetPort(&port);
  317.         SetPort(wind);
  318.         result = (*gDispatch[kind].draggable)(wind, where, modifiers);
  319.         SetPort(port);
  320.     }
  321.     
  322.     return result;
  323. }
  324.  
  325.  
  326.  
  327. /*----------------------------------------------------------------------------
  328.     HandleGrow
  329.     
  330.     Handle a mouse down event in the grow box of a window.
  331.     
  332.     Entry:    wind = pointer to window.
  333.             where = location of mouse down event, in global coordinates.
  334.             
  335.     Exit:    function result = error code.
  336. ----------------------------------------------------------------------------*/
  337.  
  338. static OSErr HandleGrow (WindowPtr wind, Point where)
  339. {
  340.     GrafPtr port;
  341.     TWindowKind kind;
  342.     OSErr err = noErr;
  343.     
  344.     kind = GetWindowKind(wind);
  345.     if (kind != kNotOurWind) {
  346.         GetPort(&port);
  347.         SetPort(wind);
  348.         err = (*gDispatch[kind].grow)(wind, where);
  349.         SetPort(port);
  350.     }
  351.     
  352.     return err;
  353. }
  354.  
  355.  
  356.  
  357. /*----------------------------------------------------------------------------
  358.     HandleDrag
  359.     
  360.     Handle a window drag operation.
  361.     
  362.     Entry:    wind = pointer to window.
  363.             where = location of mouse down event, in global coordinates.
  364. ----------------------------------------------------------------------------*/
  365.  
  366. static void HandleDrag (WindowPtr wind, Point where)
  367. {
  368.     Rect r;
  369.     GrafPtr port;
  370.     Point oldTopLeft = {0, 0};
  371.     Point newTopLeft = {0, 0};
  372.     TWindow **info;
  373.  
  374.     GetPort(&port);
  375.     SetPort(wind);
  376.     LocalToGlobal(&oldTopLeft);
  377.     r = gDesktopExtent;
  378.     InsetRect(&r, 4, 4);
  379.     DragWindow(wind, where, &r);
  380.     LocalToGlobal(&newTopLeft);
  381.     if (oldTopLeft.h != newTopLeft.h || oldTopLeft.v != newTopLeft.v) {
  382.         if (GetWindowKind(wind) != kNotOurWind) {
  383.             info = (TWindow**)GetWRefCon(wind);
  384.             (**info).windPosValid = true;
  385.             (**info).movedSinceLastSave = true;
  386.         }
  387.     }
  388.     SetPort(port);
  389. }
  390.  
  391.  
  392.  
  393. /*----------------------------------------------------------------------------
  394.     HandleActivate
  395.     
  396.     Activate or deactivate a window.
  397.     
  398.     Entry:    wind = pointer to window.
  399.             act = true to activate, false to deactivate.
  400. ----------------------------------------------------------------------------*/
  401.  
  402. void HandleActivate (WindowPtr wind, Boolean act)
  403. {
  404.     GrafPtr port;
  405.     TWindowKind kind;
  406.  
  407.     kind = GetWindowKind(wind);
  408.     if (kind != kNotOurWind) {
  409.         GetPort(&port);
  410.         SetPort(wind);
  411.         (*gDispatch[kind].activate)(wind, act);
  412.         SetPort(port);
  413.     }
  414. }
  415.  
  416.  
  417.  
  418. /*----------------------------------------------------------------------------
  419.     HandleUpdate
  420.     
  421.     Update a window.
  422.     
  423.     Entry:    wind = pointer to window.
  424. ----------------------------------------------------------------------------*/
  425.  
  426. void HandleUpdate (WindowPtr wind)
  427. {
  428.     GrafPtr port;
  429.     TWindowKind kind;
  430.     
  431.     kind = GetWindowKind(wind);
  432.     if (kind != kNotOurWind) {
  433.         GetPort(&port);
  434.         SetPort(wind);
  435.         BeginUpdate(wind);
  436.         EraseRect(&wind->portRect);
  437.         (*gDispatch[kind].update)(wind);
  438.         EndUpdate(wind);
  439.         SetPort(port);
  440.     }
  441. }
  442.  
  443.  
  444.  
  445. /*----------------------------------------------------------------------------
  446.     HandleMouseDown 
  447.     
  448.     Handle mouse down events.
  449.     
  450.     Entry:    ev = pointer to event record.
  451.     
  452.     Exit:    function result = error code.
  453. ----------------------------------------------------------------------------*/
  454.  
  455. static OSErr HandleMouseDown (EventRecord *ev)
  456. {
  457.     WindowPtr front, wind;
  458.     TWindowKind frontKind, kind;
  459.     short part;
  460.     GrafPtr port;
  461.     Point where;
  462.     short modifiers;
  463.     OSErr err = noErr;
  464.  
  465.     front = FrontWindow();
  466.     frontKind = GetWindowKind(front);
  467.     part = FindWindow(ev->where, &wind);
  468.     kind = GetWindowKind(wind);
  469.     where = ev->where;
  470.     modifiers = ev->modifiers;
  471.     
  472.     if (gLongOperation && part != inMenuBar && frontKind != kStatus) return noErr; 
  473.     
  474.     switch (part) {
  475.     
  476.         case inMenuBar:
  477.         
  478.             if (gLongOperation) SetMenusTo(kAppleOnlyAboutDisabled, 0, 0, 0, 0, 0);
  479.             err = DoCommand(MenuSelect(where), modifiers);
  480.             break;
  481.             
  482.         case inSysWindow:
  483.         
  484.             SystemClick(ev, wind);
  485.             break;
  486.             
  487.         case inDrag:
  488.         
  489.             if (front != wind && (frontKind == kStatus || frontKind == kDialog)) {
  490.                 SysBeep(0);
  491.             } else {
  492.                 HandleDrag(wind, where);
  493.             }
  494.             break;
  495.             
  496.         case inGrow:
  497.         
  498.             err = HandleGrow(wind, where);
  499.             break;
  500.             
  501.         case inGoAway:
  502.         
  503.             if (TrackGoAway(wind, where)) err = DoClose(wind);
  504.             break;
  505.             
  506.         case inZoomIn:
  507.         case inZoomOut:
  508.         
  509.             if (TrackBox(wind, where, part)) err = DoZoom(wind, part);
  510.             break;
  511.             
  512.         case inContent:
  513.         
  514.             GetPort(&port);
  515.             SetPort(wind);
  516.             GlobalToLocal(&where);
  517.             if (front == wind) {
  518.                 if (kind != kNotOurWind)
  519.                     err = (*gDispatch[kind].mouse)(wind, where, modifiers);
  520.             } else {
  521.                 if (frontKind != kStatus && frontKind != kDialog) {
  522.                     if (Draggable(wind, where, modifiers)) {
  523.                         err = (*gDispatch[kind].mouse)(wind, where, modifiers);
  524.                         gMouseDownWindow = wind;
  525.                     } else {
  526.                         SelectWindow(wind);
  527.                     }
  528.                 } else {
  529.                     SysBeep(0);
  530.                 }
  531.             }
  532.             SetPort(port);
  533.             break;
  534.             
  535.     }
  536.     
  537.     return err;
  538.     
  539. }
  540.  
  541.  
  542.  
  543. /*----------------------------------------------------------------------------
  544.     HandleMouseUp 
  545.     
  546.     Handle mouse up events.
  547.     
  548.     Entry:    ev = pointer to event record.
  549.     
  550.     Exit:    function result = error code.
  551. ----------------------------------------------------------------------------*/
  552.  
  553. static OSErr HandleMouseUp (EventRecord *ev)
  554. {
  555.     WindowPtr wind = nil;
  556.     short part;
  557.     TWindowKind frontKind;
  558.     
  559.     if (gMouseDownWindow == nil) return noErr;
  560.     frontKind = GetWindowKind(FrontWindow());
  561.     if (frontKind == kDialog || frontKind == kStatus) goto exit;
  562.     part = FindWindow(ev->where, &wind);
  563.     if (wind == gMouseDownWindow) SelectWindow(wind);
  564.  
  565. exit:
  566.  
  567.     gMouseDownWindow = nil;
  568.     return noErr;
  569. }
  570.  
  571.  
  572.  
  573. /*----------------------------------------------------------------------------
  574.     HandleKey 
  575.     
  576.     Handle key down events.
  577.     
  578.     Entry:    ev = pointer to event record.
  579.     
  580.     Exit:    function result = error code.
  581. ----------------------------------------------------------------------------*/
  582.  
  583. static OSErr HandleKey (EventRecord *ev)
  584. {
  585.     unsigned char theChar;
  586.     unsigned char theKey;
  587.     short modifiers;
  588.     long menuCmd;
  589.     WindowPtr wind;
  590.     TWindowKind kind;
  591.     GrafPtr port;
  592.     OSErr err = noErr;
  593.     
  594.     wind = FrontWindow();
  595.     kind = GetWindowKind(wind);
  596.     theChar = ev->message & charCodeMask;
  597.     theKey = (ev->message & keyCodeMask) >> 8;
  598.     modifiers = ev->modifiers;
  599.     
  600.     /*    Map F1,2,3,4 to Undo, Cut, Copy, Paste */
  601.     
  602.     if (theKey == 0x7A) {
  603.         modifiers = cmdKey;
  604.         theChar = 'Z';
  605.     } else if (theKey == 0x78) {
  606.         modifiers = cmdKey;
  607.         theChar = 'X';
  608.     } else if (theKey == 0x63) {
  609.         modifiers = cmdKey;
  610.         theChar = 'C';
  611.     } else if (theKey == 0x76) {
  612.         modifiers = cmdKey;
  613.         theChar = 'V';
  614.     }
  615.     
  616.     if (gLongOperation && kind != kStatus) {
  617.         if (theKey == escapeKeyCode || (modifiers & cmdKey) != 0 && theChar == '.') 
  618.             gCancel = true;
  619.         return noErr;
  620.     }
  621.     
  622.     #ifdef kDevelopmentVersion
  623.         if (!gLongOperation && (modifiers & cmdKey) != 0 && (modifiers & optionKey) != 0) {
  624.             if (theKey == 29) { /* cmd-opt-0 */
  625.                 DebugStr("\pYou asked for it!");
  626.                 return noErr;
  627.             }
  628.             if (theKey == 20) { /* cmd-opt-3 */
  629.                 return DisplayCache();
  630.             }
  631.             if (theKey == 21) { /* cmd-opt-4 */
  632.                 FlushCache();
  633.                 return noErr;
  634.             }
  635.             if (theKey == 23) { /* cmd-opt-5 */
  636.                 CompactCache();
  637.                 return noErr;
  638.             }
  639.         }
  640.     #endif
  641.         
  642.     if (!gLongOperation && (modifiers & cmdKey) != 0) {
  643.         if ((modifiers & optionKey) != 0) {
  644.             if (theKey == 40) theChar = 'K';
  645.             if (theKey == 37) theChar = 'L';
  646.             if (theKey == 15) theChar = 'R';
  647.             if (theKey == 01) theChar = 'S';
  648.             if (theKey == 11) theChar = 'B';
  649.         }
  650.         menuCmd = MenuKey(theChar);
  651.         if (HiWord(menuCmd) != 0) return DoCommand(menuCmd, modifiers);
  652.     } 
  653.     
  654.     if (kind != kNotOurWind) {
  655.         if (!gLongOperation) ObscureCursor();
  656.         GetPort(&port);
  657.         SetPort(wind);
  658.         err = (*gDispatch[kind].key)(wind, theChar, theKey, modifiers);
  659.         SetPort(port);
  660.     }
  661.     
  662.     return err;
  663. }
  664.  
  665.  
  666.  
  667. /*----------------------------------------------------------------------------
  668.     HandleSuspendResume 
  669.     
  670.     Handle suspend and resume events.
  671.     
  672.     Entry:    ev = pointer to event record.
  673. ----------------------------------------------------------------------------*/
  674.  
  675. static void HandleSuspendResume (EventRecord *ev)
  676. {
  677.     WindowPtr wind;
  678.     Boolean suspend;
  679.  
  680.     if ((ev->message >> 24) != 1) return;
  681.     SetCursor(&qd.arrow);
  682.     wind = FrontWindow();
  683.     suspend = (ev->message & 1) == 0;
  684.     gInBackground = suspend;
  685.     HandleActivate(wind, !suspend);
  686.     if (!suspend) AdjustWindowTitlesOnResume();
  687. }
  688.  
  689.  
  690.  
  691. /*----------------------------------------------------------------------------
  692.     HandleIdle 
  693.     
  694.     Handle idle tasks.
  695.     
  696.     Exit:    cursorRgn = cursor region for WaitNextEvent mouse moved events.
  697. ----------------------------------------------------------------------------*/
  698.  
  699. void HandleIdle (RgnHandle cursorRgn)
  700. {
  701.     WindowPtr wind;
  702.     TWindowKind kind;
  703.     GrafPtr port;
  704.     
  705.     NetIdle();
  706.     NntpIdle();
  707.     
  708.     RebuildWindowsMenu();
  709.     
  710.     wind = FrontWindow();
  711.     kind = GetWindowKind(wind);
  712.  
  713.     SetRectRgn(cursorRgn, -0x7fff, -0x7fff, 0x7fff, 0x7fff);
  714.     
  715.     if (kind == kNotOurWind) {
  716.         SetCursor(&qd.arrow);
  717.         if (IsDAWindow(wind)) {
  718.             SetMenusTo(kAppleAllEnabled, kDAFileEnabled, kDAEditEnabled, 
  719.                 kDANewsEnabled, kDASpecialEnabled,kDAWindEnabled);
  720.         } else if (!gStartupOK) {
  721.             SetMenusTo(kAppleAllEnabled, kStartupBadFileEnabled, kStartupBadEditEnabled,
  722.                 kStartupBadNewsEnabled, kStartupBadSpecialEnabled, kStartupBadWindEnabled);
  723.         } else if (wind == nil) {
  724.             SetMenusTo(kAppleAllEnabled, kNoneFileEnabled, kNoneEditEnabled, 
  725.                 kNoneNewsEnabled, kNoneSpecialEnabled, kNoneWindEnabled);
  726.         }
  727.     } else {
  728.         GetPort(&port);
  729.         SetPort(wind);
  730.         (*gDispatch[kind].idle)(wind, cursorRgn);
  731.         SetPort(port);
  732.     }
  733.     AdjustCycleWindowsCommand();
  734. }
  735.  
  736.  
  737.  
  738. /*----------------------------------------------------------------------------
  739.     HandleEvent
  740.     
  741.     Handle an event.
  742.     
  743.     Entry:    ev = pointer to event.
  744. ----------------------------------------------------------------------------*/
  745.  
  746. void HandleEvent (EventRecord *ev)
  747. {
  748.     WindowPtr front;
  749.     WindowPeek peek;
  750.     Boolean isDialog = false;
  751.     Point thisPoint;
  752.     OSErr err = noErr;
  753.  
  754.     front = FrontWindow();
  755.     if (front != nil) {
  756.         peek = (WindowPeek)front;
  757.         if (peek->windowKind == dialogKind) isDialog = true;
  758.     }
  759.     
  760.     if (isDialog) gLongOperation = false;
  761.  
  762.     switch (ev->what) {
  763.         case activateEvt:
  764.             HandleActivate((WindowPtr)ev->message, (ev->modifiers & activeFlag) != 0);
  765.             break;
  766.         case updateEvt:
  767.             HandleUpdate((WindowPtr)ev->message);
  768.             break;
  769.         case mouseDown:
  770.             err = HandleMouseDown(ev);
  771.             break;
  772.         case mouseUp:
  773.             err = HandleMouseUp(ev);
  774.             break;
  775.         case keyDown:
  776.         case autoKey:
  777.             err = HandleKey(ev);
  778.             break;
  779.         case app4Evt:
  780.             HandleSuspendResume(ev);
  781.             break;
  782.         case kHighLevelEvent:
  783.             MyAEProcessAppleEvent(ev);
  784.             gLongOperation = false;
  785.             break;
  786.         case diskEvt:
  787.             if ((ev->message & 0xffff0000) != 0) {
  788.                 DILoad();
  789.                 SetPt(&thisPoint, 120, 120);
  790.                 DIBadMount(thisPoint, ev->message);
  791.                 DIUnload();
  792.             }
  793.             break;
  794.     }
  795.     
  796.     ReportSystemError(err);
  797. }
  798.  
  799.  
  800.  
  801. /*----------------------------------------------------------------------------
  802.     Quit
  803.     
  804.     Close windows prior to quitting.
  805.     
  806.     Exit:    function result = true if OK to quit, false if error or
  807.                 canceled by user.
  808. ----------------------------------------------------------------------------*/
  809.  
  810. static Boolean Quit (void)
  811. {
  812.     WindowPtr wind;
  813.     Str255 fullGroupWindowFont;
  814.     OSErr err = noErr;
  815.     Boolean fullGroupListWasVisible;
  816.     DialogPtr dlg;
  817.     short item;
  818.     
  819.     fullGroupListWasVisible = gFullGroupWindow != nil &&
  820.         ((WindowPeek)gFullGroupWindow)->visible;
  821.     
  822.     while (true) {
  823.         wind = FrontWindow();
  824.         while (wind != nil && GetWindowKind(wind) == kNotOurWind)
  825.             wind = (WindowPtr)((WindowPeek)wind)->nextWindow;
  826.         if (wind == nil) break;
  827.         err = DoClose(wind);
  828.         if (err == userCanceledErr) goto exit1;
  829.         if (err != noErr) {
  830.             ReportSystemError(err);
  831.             err = MyGetNewDialog(kErrorDuringQuitDlg, kErrorDuringQuitDlgQuitItem, cancel, &dlg);
  832.             if (err != noErr) goto exit;
  833.             SysBeep(0);
  834.             MyModalDialog(dlg, gDialogFilterUPP, &item);
  835.             err = DoClose(dlg);
  836.             if (err != noErr) goto exit;
  837.             if (item == kErrorDuringQuitDlgQuitItem) {
  838.                 break;
  839.             } else {
  840.                 goto exit1;
  841.             }
  842.         }
  843.     }
  844.     
  845.     if (gFullGroupWindow != nil) {
  846.         gPrefs.fullGroupListVisible = fullGroupListWasVisible;
  847.         GetFontName(gFullGroupWindow->txFont, fullGroupWindowFont);
  848.         if (gPrefs.listSize != gFullGroupWindow->txSize ||
  849.             !EqualString(gPrefs.listFont, fullGroupWindowFont, false, true))
  850.                 gPrefs.maxGroupNameWidth = 0;
  851.     }
  852.     
  853.     return true;
  854.     
  855. exit:
  856.  
  857.     ReportSystemError(err);
  858.     
  859. exit1:
  860.  
  861.     if (gFullGroupWindow != nil && fullGroupListWasVisible &&
  862.         !((WindowPeek)gFullGroupWindow)->visible) 
  863.     {
  864.         MyShowWindow(gFullGroupWindow);
  865.         SetWindowsMenuShowHideFullGroupList(false);
  866.     }
  867.     return false;
  868. }
  869.  
  870.  
  871.  
  872. /*----------------------------------------------------------------------------
  873.     MainEvent 
  874.     
  875.     Main event loop.
  876. ----------------------------------------------------------------------------*/
  877.  
  878. static void MainEvent (void)
  879. {
  880.     EventRecord ev;
  881.     RgnHandle cursorRgn, rgn;
  882.     Boolean gotEvt;
  883.     WindowPtr wind;
  884.     Boolean memOK;
  885.     static Boolean haveNotifiedLowMemory = false;
  886.     OSErr err = noErr;
  887.     long sleep;
  888.     TWindowKind kind;
  889.     
  890.     cursorRgn = NewRgn();
  891.     
  892.     while (!gDone) {
  893.     
  894.         wind = MyFrontWindow();
  895.         if (GetWindowKind(wind) == kStatus) DoClose(wind);
  896.     
  897.         if (gLongOperation && gInBackground) NotifyUser();
  898.     
  899.         memOK = RecoverReserveMemory();
  900.         if (memOK) {
  901.             haveNotifiedLowMemory = false;
  902.         } else if (!haveNotifiedLowMemory) {
  903.             CautionMessageNumber(kStrMemoryLow);
  904.             haveNotifiedLowMemory = true;
  905.         }
  906.         EndCriticalMemorySequence(false);
  907.         
  908.         HandleIdle(cursorRgn);
  909.     
  910.         gCancel = gLongOperation = gInDialog = false;
  911.         gDragPostProcessor = nil;
  912.         
  913.         if (gInBackground) {
  914.             /* sleep = 0x7fffffff; - doesn't work: drag text to message
  915.                wind while in background, window doesn't update! */
  916.             sleep = GetCaretTime();
  917.             rgn = nil;
  918.         } else {
  919.             kind = GetWindowKind(FrontWindow());
  920.             if (kind == kGroup || kind == kSubject) {
  921.                 /*sleep = 0x7fffffff;*/
  922.                 sleep = GetCaretTime();
  923.                 rgn = nil;
  924.             } else {
  925.                 sleep = GetCaretTime();
  926.                 rgn = cursorRgn;
  927.             }
  928.         }
  929.         
  930.         gotEvt = WaitNextEvent(everyEvent, &ev, sleep, rgn);
  931.         
  932.         if (gotEvt) {
  933.             gPrevEvent = gCurEvent;
  934.             gCurEvent = ev;
  935.             HandleEvent(&ev);
  936.         }
  937.         
  938.         if (gDragErr != noErr) {
  939.             ReportSystemError(gDragErr);
  940.             gDragErr = noErr;
  941.         }
  942.         
  943.         if (gDragPostProcessor != nil) {
  944.             err = (*gDragPostProcessor)();
  945.             ReportSystemError(err);
  946.         }
  947.         
  948.         if (gDone) gDone = Quit();
  949.         
  950.     }
  951. }
  952.  
  953.  
  954.  
  955. /*----------------------------------------------------------------------------
  956.     GiveTime
  957.     
  958.     Give time to other processes during long operations. Check for 
  959.     user cancels. Spin the beachball cursor.
  960.     
  961.     Entry:    waiting = true if waiting for MacTCP or something else - 
  962.                 yield CPU continuously to other processes while
  963.                 waiting.
  964.             waiting = false if busy doing some kind of computation -
  965.                 yield CPU to other processes only once every 5 ticks.
  966.     
  967.     Exit:    function result = error code (userCanceledErr if canceled
  968.                 by user, else noErr).
  969. ----------------------------------------------------------------------------*/
  970.  
  971. OSErr GiveTime (Boolean waiting)
  972. {
  973.     EventRecord ev;
  974.     Boolean gotEvt;
  975.     static long nextTime = 0;
  976.     static DialogPtr waitForDNRDlg;
  977.         
  978.     if (!gLongOperation) {
  979.         HiliteMenu(0);
  980.         SpinCursor(0);
  981.         ShowCursor();
  982.         gLongOperation = true;
  983.     }
  984.  
  985.     if (TickCount() >= nextTime) {
  986.         #ifdef powerc
  987.             SpinCursor(32);    /* joke - spin cursor twice as fast on Power Macs */
  988.         #else
  989.             SpinCursor(16);
  990.         #endif
  991.         nextTime = TickCount() + 5;
  992.         waiting = true;
  993.     }
  994.     
  995.     if (waiting) {
  996.         gotEvt = WaitNextEvent(everyEvent & ~highLevelEventMask & ~diskMask, 
  997.             &ev, 0, nil);
  998.         if (gotEvt) HandleEvent(&ev);
  999.     }
  1000.     
  1001.     if (gCancel && waitForDNRDlg == nil && NetDNROperationInProgress()) {
  1002.         MyGetNewDialog(kWaitForDNRDlg, 0, 0, &waitForDNRDlg);
  1003.         if (waitForDNRDlg != nil) {
  1004.             RestoreMovableModalDialogPosition(waitForDNRDlg, gPrefs.waitForDNRLoc);
  1005.             MyShowWindow(waitForDNRDlg);
  1006.         }
  1007.     }
  1008.     
  1009.     if (waitForDNRDlg != nil && !NetDNROperationInProgress()) {
  1010.         SaveMovableModalDialogPosition(waitForDNRDlg, &gPrefs.waitForDNRLoc);
  1011.         DoClose(waitForDNRDlg);
  1012.         waitForDNRDlg = nil;
  1013.     }
  1014.     
  1015.     return gCancel ? userCanceledErr : noErr;
  1016. }
  1017.  
  1018.  
  1019.  
  1020. /*----------------------------------------------------------------------------
  1021.     ForceCodeHigh 
  1022.         
  1023.     Load all unloaded CODE resources, force them to high memory, 
  1024.     and lock them.
  1025.     
  1026.     Does nothing in Power PC mode.
  1027. ----------------------------------------------------------------------------*/
  1028.  
  1029. static void ForceCodeHigh (void)
  1030. {
  1031.     Handle h;
  1032.     short refNum, curRefNum, i, rID;
  1033.     ResType rType;
  1034.     Str255 rName;
  1035.     OSErr err = noErr;
  1036.     
  1037.     #ifdef powerc
  1038.         return;
  1039.     #endif
  1040.     
  1041.     h = GetResource('CODE', 1);
  1042.     if (h == nil) return;
  1043.     refNum = HomeResFile(h);
  1044.     curRefNum = CurResFile();
  1045.     UseResFile(refNum);
  1046.     SetResLoad(false);
  1047.     for (i = 1; ; i++) {
  1048.         h = Get1IndResource('CODE', i);
  1049.         if (h == nil) break;
  1050.         GetResInfo(h, &rID, &rType, rName);
  1051.         if (rID != 0) {
  1052.             LoadResource(h);
  1053.             HLockHi(h);
  1054.         }
  1055.     }
  1056.     SetResLoad(true);
  1057.     UseResFile(curRefNum);
  1058.     
  1059.     /* Force segment loader to know that ANSI seg is loaded. */
  1060.     
  1061.     strlen("");
  1062. }
  1063.  
  1064.  
  1065.  
  1066. /*----------------------------------------------------------------------------
  1067.     TrapType 
  1068.         
  1069.     Get the type of a trap.
  1070.     
  1071.     Entry:    theTrap = trap number.
  1072.     
  1073.     Exit:    function result = ToolTrap or OSTrap.
  1074. ----------------------------------------------------------------------------*/
  1075.  
  1076. static TrapType GetTrapType (unsigned long theTrap)
  1077. {
  1078.     return (theTrap & 0x0800) != 0 ? ToolTrap : OSTrap;
  1079. }
  1080.  
  1081.  
  1082.  
  1083. /*----------------------------------------------------------------------------
  1084.     TrapAvailable 
  1085.         
  1086.     Check to see if a trap is available.
  1087.     
  1088.     Entry:    theTrap = trap number.
  1089.     
  1090.     Exit:    function result = true if trap available.
  1091. ----------------------------------------------------------------------------*/
  1092.     
  1093. static Boolean TrapAvailable (unsigned long trap)
  1094. {
  1095.     TrapType trapType;
  1096.     unsigned long numToolBoxTraps;
  1097.  
  1098.     if (NGetTrapAddress(_InitGraf, ToolTrap) == 
  1099.         NGetTrapAddress(0xAA6E, ToolTrap))
  1100.     {
  1101.         numToolBoxTraps = 0x200;
  1102.     } else {
  1103.         numToolBoxTraps = 0x400;
  1104.     }
  1105.  
  1106.     trapType = GetTrapType(trap);
  1107.     if (trapType == ToolTrap) {
  1108.         trap = trap & 0x07ff;
  1109.         if (trap >= numToolBoxTraps) trap = _Unimplemented;
  1110.     }
  1111.     return NGetTrapAddress(trap, trapType) != 
  1112.         NGetTrapAddress(_Unimplemented, ToolTrap);
  1113.  
  1114. }
  1115.  
  1116.  
  1117.  
  1118. /*----------------------------------------------------------------------------
  1119.     Init 
  1120.         
  1121.     Initialize the program.
  1122.     
  1123.     Exit:    function result = true if startup OK up through establishing a
  1124.                 connection with the news server and getting the full
  1125.                 group list (either from the prefs file or from the
  1126.                 server), false otherwise.
  1127. ----------------------------------------------------------------------------*/
  1128.  
  1129. static Boolean Init (void)
  1130. {
  1131.     EventRecord ev;
  1132.     AppleEvent firstEvent = {0, nil};
  1133.     AppleEvent firstReply = {0, nil};
  1134.     CStr255 msg;
  1135.     Boolean mightHaveNewGroups = true;
  1136.     short sizeofTPrefRec;
  1137.     long systemVersion, dragMgrAttr;
  1138.     OSErr err = noErr;
  1139.     Boolean savedNoNewConnection;
  1140.     Handle mbar;
  1141.     CStr255 fmt;
  1142.     ProcessSerialNumber myPSN, frontPSN;
  1143.     Boolean inFront;
  1144.     TGroup **newGroupsArray = nil;
  1145.     short numNewGroups = 0;
  1146.     WindowPtr wind;
  1147.     Handle h;
  1148.     
  1149.     /* Initialize the toolbox. */
  1150.  
  1151.     InitGraf(&qd.thePort);
  1152.     InitFonts();
  1153.     InitWindows();
  1154.     InitMenus();
  1155.     TEInit();
  1156.     InitDialogs(nil);
  1157.     InitCursor();
  1158.  
  1159.     /* Allocate a 1K memory block, force it to the top of the heap, and lock it.
  1160.        This block is not used for anything - it is put at the top of the heap
  1161.        as a sacrifice because sometimes MacSLIP's VBL task trashes the end of 
  1162.        the heap. */
  1163.      
  1164.     h = NewHandle(1000);
  1165.     if (h != nil) {
  1166.         MoveHHi(h);
  1167.         HLock(h);
  1168.     }
  1169.     
  1170.     /* Check for System 7.0 or later. */
  1171.  
  1172.     if (!TrapAvailable(_Gestalt)) {
  1173.         StopAlert(kNeedSystem7AlertID, nil);
  1174.         ExitToShell();
  1175.     }
  1176.     err = Gestalt(gestaltSystemVersion, &systemVersion);
  1177.     if (err != noErr || systemVersion < 0x0700) {
  1178.         StopAlert(kNeedSystem7AlertID, nil);
  1179.         ExitToShell();
  1180.     }
  1181.     
  1182.     /* In 68k mode, preload and force all CODE resources to locked high memory.
  1183.        This strategy is used instead of unloadseg calls to avoid fragmenting
  1184.        the heap. */
  1185.  
  1186.     ForceCodeHigh();
  1187.     
  1188.     /* Intialize the memory utilities. */
  1189.     
  1190.     InitMemUtil(40000, 40000);
  1191.     
  1192.     /* Initialize the window handling module method dispatch tables. */
  1193.     
  1194.     InitGroupDispatchTable();
  1195.     InitSubjectDispatchTable();
  1196.     InitArticleDispatchTable();
  1197.     InitMessageDispatchTable();
  1198.     InitTextDispatchTable();
  1199.     InitStatusDispatchTable();
  1200.     InitDialogDispatchTable();
  1201.     InitDummyDispatchTable();
  1202.     
  1203.     /* Intialize miscellaneous UPPs. */
  1204.     
  1205.     sfutil_InitUPP();
  1206.     drawutil_InitUPP();
  1207.     windutil_InitUPP();
  1208.     print_InitUPP();
  1209.     prefsdlog_InitUPP();
  1210.     net_InitUPP();
  1211.     ldef_InitUPP();
  1212.     
  1213.     /* Create a pool of 50 window records in locked high memory, again to
  1214.        avoid later heap fragmentation. */
  1215.     
  1216.     err = InitializeWindowRecordStorage(50);
  1217.     if (err != noErr) goto exit1;
  1218.     
  1219.     /* Initialize the spinning beachball cursor. */
  1220.     
  1221.     InitCursorCtl(nil);
  1222.     
  1223.     /* Flush the event queue. */
  1224.  
  1225.     FlushEvents(everyEvent,0);
  1226.     EventAvail(everyEvent, &ev);
  1227.     
  1228.     /* Check for the Drag Manager and TextEdit outline hilite feature. */
  1229.  
  1230.     err = Gestalt(gestaltDragMgrAttr, &dragMgrAttr);
  1231.     gHaveDragMgr = err == noErr && (dragMgrAttr & (1L << gestaltDragMgrPresent)) != 0;
  1232.     #ifdef powerc
  1233.         gHaveDragMgr = gHaveDragMgr && 
  1234.             (dragMgrAttr & (1L << gestaltPPCDragLibPresent)) != 0
  1235.             && (Ptr)InstallTrackingHandler != kUnresolvedSymbolAddress;
  1236.     #endif
  1237.     gHaveTEOutlineHilite = HaveTEOutlineHiliteFeature();
  1238.     
  1239.     /* Get the desktop extent and ibeam and watch cursors. Initialize SpinCursor. */
  1240.     
  1241.     gDesktopExtent = (**GetGrayRgn()).rgnBBox;
  1242.     gIBeamCurs = **(GetCursor(iBeamCursor));
  1243.     gWatchCurs = **(GetCursor(watchCursor));
  1244.     SpinCursor(0);
  1245.     
  1246.     /* Create and draw the menu bar. */
  1247.     
  1248.     mbar = GetNewMBar(kMBarID);
  1249.     SetMenuBar(mbar);
  1250.     AppendResMenu(GetMenuHandle(kAppleMenu), 'DRVR');
  1251.     DrawMenuBar();
  1252.     
  1253.     /* Figure out whether we were launched in the background or foreground. */
  1254.  
  1255.     GetCurrentProcess(&myPSN);
  1256.     GetFrontProcess(&frontPSN);
  1257.     SameProcess(&myPSN, &frontPSN, &inFront);
  1258.     gInBackground = !inFront;
  1259.     
  1260.     /* Check to see if we screwed up the size of the prefs struct. This is
  1261.        for sanity testing during development. */
  1262.  
  1263.     sizeofTPrefRec = sizeof(TPrefRec);
  1264.     if (sizeofTPrefRec != kTPrefRecSize) {
  1265.         GetCString(kStrSizeofTPrefRecBadFmt, fmt);
  1266.         sprintf(msg, fmt, sizeofTPrefRec, kTPrefRecSize);
  1267.         ErrorMessage(msg);
  1268.         ExitToShell();
  1269.     }
  1270.     
  1271.     /* Initialize Apple event handling. */
  1272.  
  1273.     err = InitializeAppleEvents(&firstEvent, &firstReply);
  1274.     if (err != noErr) goto exit1;
  1275.     
  1276.     /* Read the preferences file */
  1277.     
  1278.     err = ReadPrefs(&firstEvent);
  1279.     if (err != noErr) goto exit1;
  1280.     
  1281.     /* Open the log file. */
  1282.  
  1283.     if (gPrefs.logActionsToFile) OpenLogFile();
  1284.     
  1285.     /* Open MacTCP. */
  1286.  
  1287.     err = DisplayStatusMessageNumber(kStrOpeningMacTCPStatusMsg);
  1288.     if (err != noErr) goto exit1;
  1289.  
  1290.     err = NetInit(GiveTime, Log, 2);
  1291.     if (err != noErr && err != userCanceledErr) {
  1292.         ErrorMessageNumber(kStrCouldNotOpenMacTCP);
  1293.         err = userCanceledErr;
  1294.     }
  1295.     if (err != noErr) goto exit1;
  1296.     
  1297.     /* Open the initial connection to the news server. */
  1298.  
  1299.     savedNoNewConnection = gPrefs.noNewConnection;
  1300.     gPrefs.noNewConnection = true;
  1301.     err = StartNNTP();
  1302.     gPrefs.noNewConnection = savedNoNewConnection;
  1303.     ResetNewsServerOptions();
  1304.     if (err != noErr) goto exit1;
  1305.     
  1306.     /* Get the full group list from the server if necessary. */
  1307.     
  1308.     if (gNumGroups == 0) {
  1309.         err = DoRebuildFullGroupList();
  1310.         if (err != noErr) goto exit1;
  1311.         mightHaveNewGroups = false;
  1312.     }
  1313.     
  1314.     /* Create the full group window. */
  1315.     
  1316.     err = MakeFullGroupWindow(gFullGroupArray, gNumGroups, &gFullGroupWindow);
  1317.     if (err != noErr) goto exit1;
  1318.     
  1319.     /* Check for new groups if necessary and requested by the user. */
  1320.     
  1321.     if (mightHaveNewGroups && gPrefs.checkForNewGroups) {
  1322.         err = CheckForNewGroups(&newGroupsArray, &numNewGroups);
  1323.         if (err != noErr && err != userCanceledErr) goto exit2;
  1324.         gCancel = false;
  1325.     }
  1326.     
  1327.     /* Auto-fetch a newsrc file from a remote host if requested by the user. */
  1328.  
  1329.     if (gPrefs.autoFetchNewsrc) {
  1330.         err = AutoFetchNewsrcFromHost();
  1331.         if (err != noErr && err != userCanceledErr) goto exit2;
  1332.         gCancel = false;
  1333.     }
  1334.     
  1335.     /* Handle the initial Apple event. */
  1336.     
  1337.     MyAEResumeTheCurrentEvent(&firstEvent, &firstReply, 
  1338.         (AEEventHandlerUPP)kAEUseStandardDispatch, 0);
  1339.     firstEvent.dataHandle = nil;
  1340.     
  1341.     /* If there are new groups, create the new groups window. */
  1342.     
  1343.     if (numNewGroups > 0) {
  1344.         err = MakeNewGroupsWindow(newGroupsArray, numNewGroups, &wind);
  1345.         if (err != noErr) goto exit2;
  1346.     }
  1347.     
  1348.     return true;
  1349.     
  1350. exit1:
  1351.  
  1352.     if (firstEvent.dataHandle != nil)
  1353.         MyAEResumeTheCurrentEvent(&firstEvent, &firstReply, 
  1354.             (AEEventHandlerUPP)kAENoDispatch, 0);
  1355.     EndNNTP();
  1356.     ReportSystemError(err);
  1357.     ErrorMessageNumber(kStrStartupError);
  1358.     return false;
  1359.     
  1360. exit2:
  1361.  
  1362.     if (firstEvent.dataHandle != nil)
  1363.         MyAEResumeTheCurrentEvent(&firstEvent, &firstReply, 
  1364.             (AEEventHandlerUPP)kAENoDispatch, 0);
  1365.     ReportSystemError(err);
  1366.     return true;
  1367. }
  1368.  
  1369.  
  1370.  
  1371. /*----------------------------------------------------------------------------
  1372.     Term 
  1373.     
  1374.     Terminate the program.
  1375. ----------------------------------------------------------------------------*/
  1376.  
  1377. static void Term (void)
  1378. {
  1379.     OSErr err = noErr;
  1380.  
  1381.     SetToolTrapAddress(gOldExitToShellUPP, _ExitToShell);
  1382.     err = WritePrefs();
  1383.     ReportSystemError(err);
  1384.     if (gPrefs.logActionsToFile) CloseLogFile();    
  1385.     DeleteTemporaryFiles(kNewsWatcherSignature);
  1386.     EndNNTP();
  1387.     NetTerm();
  1388.     MyICStop();
  1389. }
  1390.  
  1391.  
  1392.  
  1393. /*----------------------------------------------------------------------------
  1394.     MyExitToShell 
  1395.     
  1396.     ExitToShell trap patch.
  1397.     
  1398.     This patch makes sure that EndNNTP and NetTerm are called even if we
  1399.     blow up. This closes our news server stream and keeps MacTCP happy.
  1400.     It doesn't always work, but it helps sometimes.
  1401. ----------------------------------------------------------------------------*/
  1402.  
  1403. static void MyExitToShell (void)
  1404. {
  1405.     SetCurrentA5();
  1406.     SetToolTrapAddress(gOldExitToShellUPP, _ExitToShell);
  1407.     EndNNTP();
  1408.     NetTerm();
  1409.     ExitToShell();
  1410. }
  1411.  
  1412.  
  1413.  
  1414. /*----------------------------------------------------------------------------
  1415.     PatchExitToShell 
  1416.     
  1417.     Install a patch on the ExitToShell trap.
  1418. ----------------------------------------------------------------------------*/
  1419.  
  1420. static void PatchExitToShell (void)
  1421. {
  1422.     gOldExitToShellUPP = GetToolTrapAddress(_ExitToShell);
  1423.     gMyExitToShellUPP = 
  1424.         NewRoutineDescriptor((ProcPtr)MyExitToShell, kPascalStackBased, GetCurrentISA());
  1425.     SetToolTrapAddress(gMyExitToShellUPP, _ExitToShell);
  1426. }
  1427.  
  1428.  
  1429.  
  1430. /*----------------------------------------------------------------------------
  1431.     main 
  1432.     
  1433.     Main entry point.
  1434. ----------------------------------------------------------------------------*/
  1435.  
  1436. void main (void)
  1437. {
  1438.     short i;
  1439.  
  1440.     SetApplLimit(GetApplLimit() - 20000);
  1441.     MaxApplZone();
  1442.     for (i = 0; i < 20; i++) MoreMasters();
  1443.     PatchExitToShell();
  1444.     gStartupOK = Init();
  1445.     gStartingUp = false;
  1446.     MainEvent();
  1447.     Term();
  1448. }
  1449.